package com.renu.jdependencyanalyser.core;

import com.renu.jdependencyanalyser.cons.ProjectCons;
import com.renu.jdependencyanalyser.model.ClassModel;
import com.renu.jdependencyanalyser.model.JarModel;
import com.renu.jdependencyanalyser.model.PackageModel;
import com.renu.jdependencyanalyser.utils.MyAsmUtil;
import com.renu.jdependencyanalyser.utils.MyFileUtil;
import com.renu.jdependencyanalyser.utils.MyProjectUtil;
import soot.Scene;
import soot.SootMethod;
import soot.jimple.toolkits.callgraph.CallGraph;
import soot.jimple.toolkits.callgraph.Edge;

import java.io.File;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

public class ProjectBorderGene {

    private ProjectInfo projectInfo;

    private Set<String> projectClasses;
    private Set<String> classPaths;

    public ProjectInfo getProjectInfo() {
        return projectInfo;
    }

    public Set<String> getProjectClasses() {
        return projectClasses;
    }

    public ProjectBorderGene(ProjectInfo projectInfo) {

        this.projectInfo = projectInfo;
        this.projectClasses = new HashSet<>();


        buildInnerClassPck();

        buildOuterClassPckJar();

        parseClassFile();

        System.out.println("======");

    }

    private void parseClassFile() {
        // 读取字节码文件
        for (String classPath : classPaths) {
            String classSig = MyFileUtil.extractClassSigFromClassPath(classPath);
            ClassModel classModel = projectInfo.getClassModel(classSig);
            MyAsmUtil.readClass(classPath,classModel);
        }
    }


    private void buildOuterClassPckJar() {
        String jarPath = MyProjectUtil.projectBasePath + ProjectCons.JAR_PATH;
        //
        HashSet<JarModel> jars = getJarsFromDir(new File(jarPath));

        for (JarModel jar : jars) {
            //jar
            projectInfo.addOuterJar(jar);

            Set<String> classPaths = MyFileUtil.findClassePathsFromJar(jar.getJarPath());
            for (String classPath : classPaths) {
                String classSig = MyFileUtil.classPath2ClassSig(classPath);
                String pckName = MyFileUtil.classSig2PckName(classSig);

                projectInfo.addOuterClass(new ClassModel(classSig));
                projectInfo.addOuterPck(new PackageModel(jar.getJarSig(),pckName));

                String pckSig =  pckName;


                projectInfo.outerClassBindPck(classSig,pckSig);
                projectInfo.outerPackageBindJar(jar.getJarSig(),pckSig);

            }

        }

    }

    @Deprecated
    public   void buildInvokeRelation(HashSet<SootMethod> mySootMethod, HashSet<SootMethod> sootMethods) {
        CallGraph cg = Scene.v().getCallGraph();

        // 从我的项目出发
        for (SootMethod sootMethod : mySootMethod) {
            if (sootMethod.isAbstract())
                continue;

            // 设置outMthds、inMthds
            String srcSig = sootMethod.getSignature();
            Iterator<Edge> outIte = cg.edgesOutOf(sootMethod);
            while (outIte.hasNext()) {
                Edge edge = outIte.next();
                if (!MyProjectUtil.isJDKClassOrBasicType(edge.tgt().getDeclaringClass().getName())) {
                    SootMethod tgt = edge.tgt();
                    String tgtSig = tgt.getSignature();
                    //添加软件边界
                    projectInfo.addBorderMethod(tgtSig);
                    System.out.println(srcSig+"-->"+tgtSig);
                    // 添加类之间的关系
                    //boolean flog = projectInfo.bindInOut(srcSig, tgtSig);
                    //if (flog)
                    //    buildInvokeChain(cg,tgt,1);

            }
            }
        }
    }



    @Deprecated
    public   void builderOuter() {
        String jarPath = MyProjectUtil.projectBasePath + ProjectCons.JAR_PATH;
        //
        HashSet<JarModel> jars = getJarsFromDir(new File(jarPath));

        for (JarModel jar : jars) {
            //jar
            projectInfo.addJar(jar);

            Set<String> classPaths = MyFileUtil.findClassePathsFromJar(jar.getJarPath());
            for (String classPath : classPaths) {
                String classSig = MyFileUtil.classPath2ClassSig(classPath);
                String pckName = MyFileUtil.classSig2PckName(classSig);

                projectInfo.addPck(new PackageModel(jar.getJarSig(),pckName));

                String pckSig = jar.getJarSig() +":" + pckName;

                ClassModel classModel = projectInfo.getClassModel(classSig);

                projectInfo.classBindPck(classSig,pckSig);
                projectInfo.packageBindJar(jar.getJarSig(),pckSig);

            }

        }

    }


    /**
     * 已知classModel
     */
    @Deprecated
    public   void buildInner() {
        JarModel jarModel = new JarModel(ProjectCons.PROJECT_SELF_NAME, "");
        projectInfo.addJar(jarModel);
        String innerPath= MyProjectUtil.projectBasePath + File.separator + "src";

        Set<String> javaPaths = MyFileUtil.findxxPath(new File(innerPath),".java");
        for (String javaPath : javaPaths) {
           String classSig  = MyFileUtil.extractClassSigFromJavaPath(javaPath);
           String pckName = MyFileUtil.classSig2PckName(classSig);
           String pckSig = ProjectCons.PROJECT_SELF_NAME +":"+ pckName;


           projectInfo.addPck(new PackageModel(ProjectCons.PROJECT_SELF_NAME,pckName));
           projectInfo.classBindPck(classSig,pckSig);
           projectInfo.pckBindJar(pckSig,ProjectCons.PROJECT_SELF_NAME);

        }




    }

    /**
     * 只分析当前项目 ;
     */


    /**
     * 读取class 路径 获取pck,class ,jar
     */
    private void buildInnerClassPck() {
        JarModel jarModel = new JarModel(ProjectCons.PROJECT_SELF_NAME, "");
        projectInfo.addJar(jarModel);
        String innerPath= MyProjectUtil.projectBasePath + ProjectCons.SCR_INPUT_PATH;

        classPaths = MyFileUtil.findxxPath(new File(innerPath),".class");
        for (String javaPath : classPaths) {
            String classSig  = MyFileUtil.extractClassSigFromClassPath(javaPath);
            String pckName = MyFileUtil.classSig2PckName(classSig);
            String pckSig = ProjectCons.PROJECT_SELF_NAME +":"+ pckName;

            projectInfo.addPck(new PackageModel(ProjectCons.PROJECT_SELF_NAME,pckName));
            projectInfo.addClass(new ClassModel(classSig));

            projectInfo.classBindPck(classSig,pckSig);
            projectInfo.pckBindJar(pckSig,ProjectCons.PROJECT_SELF_NAME);

            projectClasses.add(classSig);
        }
    }

    /**
     * 未知classModel,获取项目class、package、jar
     */
    @Deprecated
    public void buildInnerClass(){
        JarModel jarModel = new JarModel(ProjectCons.PROJECT_SELF_NAME, "");
        projectInfo.addJar(jarModel);
        String innerPath= MyProjectUtil.projectBasePath + ProjectCons.SCR_INPUT_PATH;

        Set<String> javaPaths = MyFileUtil.findxxPath(new File(innerPath),".class");
        for (String javaPath : javaPaths) {
            String classSig= MyFileUtil.extractClassSigFromClassPath(javaPath);
            //String classSig=  = MyFileUtil.extractClassSigFromJavaPath(javaPath);
            String pckName = MyFileUtil.classSig2PckName(classSig);
            String pckSig = ProjectCons.PROJECT_SELF_NAME +":"+ pckName;

            projectInfo.addPck(new PackageModel(ProjectCons.PROJECT_SELF_NAME,pckName));
            projectInfo.addClass(new ClassModel(classSig));

            projectInfo.classBindPck(classSig,pckSig);
            projectInfo.pckBindJar(pckSig,ProjectCons.PROJECT_SELF_NAME);

            projectClasses.add(classSig);
        }



    }


    /**
     * 得到jar包的jarSig和路径
     * @param dir
     * @return
     */
    private  HashSet<JarModel> getJarsFromDir(File dir) {
        HashSet<JarModel> jarModels = new HashSet<>();
        File[] files = dir.listFiles();
        for (File f : files) {
            String fPath = f.getAbsolutePath();
            String fName = f.getName();
            if (fName.endsWith(".jar")){
                jarModels.add(new JarModel(fName,fPath));
            }
        }
        for (File file : files) {
            if (file.isDirectory()){
                jarModels.addAll(getJarsFromDir(file));
            }
        }
        return jarModels;


    }
}
